<?php
/**
 * @version		1.23 January 15, 2012
 * @author		RocketTheme http://www.rockettheme.com
 * @copyright 	Copyright (C) 2007 - 2012 RocketTheme, LLC
 * @license		http://www.gnu.org/licenses/gpl-2.0.html GNU/GPLv2 only
 */

class GantryAssignmentWalker extends Walker {
    var $tree_type = array('post_type', 'taxonomy', 'custom');
    var $db_fields = array('parent' => 'parent_id', 'id' => 'id');

    /**
     * @see Walker::start_lvl()
     * @since 3.0.0
     *
     * @param string $output Passed by reference. Used to append additional content.
     * @param int $depth Depth of page. Used for padding.
     */
    function start_lvl(&$output, $depth) {
        $indent = str_repeat("\t", $depth);
        $output .= "\n$indent<ul class=\"sub-menu\">\n";
    }

    /**
     * @see Walker::end_lvl()
     * @since 3.0.0
     *
     * @param string $output Passed by reference. Used to append additional content.
     * @param int $depth Depth of page. Used for padding.
     */
    function end_lvl(&$output, $depth) {
        $indent = str_repeat("\t", $depth);
        $output .= "$indent</ul>\n";
    }

    /**
     * @see Walker::start_el()
     * @since 3.0.0
     *
     * @param string $output Passed by reference. Used to append additional content.
     * @param object $item Menu item data object.
     * @param int $depth Depth of menu item. Used for padding.
     * @param int $current_page Menu item ID.
     * @param object $args
     */
    function start_el(&$output, $item, $depth, $args) {
        global $wp_query;
        global $gantry_override_assignment_info;
        $indent = ($depth) ? str_repeat("\t", $depth) : '';

        $class_names = $value = '';

        $classes = empty($item->classes) ? array() : (array) $item->classes;

        $assigns = $args->assignments;

        if (isset($assigns[$item->archetype]) && isset($assigns[$item->archetype][$item->type]) &&
                ((is_array($assigns[$item->archetype][$item->type]) && in_array($item->id, $assigns[$item->archetype][$item->type])) ||
                        (is_bool($assigns[$item->archetype][$item->type]) && $assigns[$item->archetype][$item->type] === true))
        ) {
            array_push($classes, "added");
        }

        $class_names = join(' ', apply_filters('nav_menu_css_class', array_filter($classes), $item));
        $class_names = ' class="' . esc_attr($class_names) . '"';

        $output .= $indent . '<li id="' . $item->type . '-menu-item-' . $item->id . '"' . $value . $class_names . '>';

        $item_pass = $item->archetype . '::' . $item->type;
        $item_pass .= (isset($item->id)) ? '::' . $item->id : '';

        if (
                (isset($item->id) && isset($args->assignments[$item->archetype][$item->type]) && is_array($args->assignments[$item->archetype][$item->type]) && in_array($item->id, $args->assignments[$item->archetype][$item->type]))
                ||
                (!isset($item->id) && isset($args->assignments[$item->archetype][$item->type]) && $args->assignments[$item->archetype][$item->type] == true)
        ) {
            $gantry_override_assignment_info[$item_pass] = $item;
        }

        $attributes = !empty($item->title) ? ' title="' . esc_attr($item->title) . '"' : '';
        $attributes .= ' rel="' . $item_pass . '"';
        $item->url = null;
        $attributes .= !empty($item->url) ? ' href="' . esc_attr($item->url) . (!strstr(esc_attr($item->url), '?') ? '?' : '&amp;') . 'TB_iframe=true&amp;height=600&amp;width=800"' : '';
        if (empty($item->url)) $attributes .= ' href="#"' . "\n";

        $item_output = $args->before;
        $item_output .= '<label class="rokchecks menu-item-' . $item->id . '">' . "\n";
        $item_output .= '	<input class="assignment-checkbox" type="checkbox" name="menu-item-' . $item->id . '" value="' . $item->id . '" />' . "\n";
        $item_output .= '</label>' . "\n";
        $item_output .= ' 	<a class="' . (empty($item->url) ? 'no-link-item' : 'thickbox') . '"' . $attributes . '>' . "\n";
        $item_output .= $args->link_before . apply_filters('the_title', $item->title, $item->id) . $args->link_after;
        $item_output .= '</a>';
        $item_output .= $args->after;

        $output .= apply_filters('walker_gantry_assignments_start_el', $item_output, $item, $depth, $args);
    }

    /**
     * @see Walker::end_el()
     * @since 3.0.0
     *
     * @param string $output Passed by reference. Used to append additional content.
     * @param object $item Page data object. Not used.
     * @param int $depth Depth of page. Not Used.
     */
    function end_el(&$output, $item, $depth) {
        $output .= "</li>\n";
    }
}


function gantry_assignment_post_type_meta_boxes() {
    global $gantry_override_types;

    $post_types = get_post_types(array('show_ui' => true), 'object');

    if (!$post_types)
        return;

    foreach ($post_types as $post_type) {
        $post_type = apply_filters('gantry_assignment_meta_box_object', $post_type);
        if ($post_type) {
            $type = new AssignmentType();
            $type->name = $post_type->name;
            $type->archetype = "post_type";
            $type->type_label = $post_type->labels->name;
            $type->type = $post_type->name;
            $type->single_label = $post_type->labels->singular_name;
            $gantry_override_types[$type->archetype . '::' . $type->type] = $type;
            add_meta_box($type->archetype . '_' . $type->type, $type->type_label, 'gantry_assignment_item_post_type_meta_box', 'gantry_assignments', 'panel', 'default', $type);

            $taxonomies = get_object_taxonomies( $post_type->name);
            if (!empty($taxonomies)){
                $type = new AssignmentType();
                $type->name = $post_type->name;
                $type->archetype = "taxonomy";
                $type->type_label = sprintf(_g('%s: Terms'), $post_type->labels->name);
                $type->type = $post_type->name;
                $type->single_label = sprintf(_g('%s: Term'), $post_type->labels->singular_name);
                $gantry_override_types[$type->archetype . '::' . $type->type] = $type;
                add_meta_box($type->archetype . '_' . $type->type, $type->type_label, 'gantry_assignment_item_post_type_taxonomies_meta_box', 'gantry_assignments', 'panel', 'default', $type);
            }


        }
    }
}

/**
 * Displays a metabox for a post type menu item.
 *
 * @since 3.0.0
 *
 * @param string $object Not used.
 * @param string $post_type The post type object.
 */
function gantry_assignment_item_post_type_meta_box($object, $post_type, $assignments) {
    global $_nav_menu_placeholder, $nav_menu_selected_id;

    $post_type_name = $post_type['args']->type;

    $args = array(
        'order' => 'ASC',
        'orderby' => 'title',
        'post_type' => $post_type_name,
        'suppress_filters' => true,
        'update_post_term_cache' => false,
        'update_post_meta_cache' => false,
        'posts_per_page' => -1
    );

    if (isset($post_type['args']->_default_query))
        $args = array_merge($args, (array) $post_type['args']->_default_query);

    $get_posts = new WP_Query;
    $posts = $get_posts->query($args);

    if (!$get_posts->post_count) {
        echo '<p>' . _g('No items.') . '</p>';
        return;
    }
    $newposts = array();
    foreach ($posts as $post_key => $post) {
        $item = new AssignmentItem();
        $item->archetype = $post_type['args']->archetype;
        $item->type = $post_type_name;
        $item->id = $post->ID;
        $item->single_label = $post_type['args']->single_label;
        $item->title = $post->post_title;
        $item->preview_url = get_permalink($item->id);
        $item->parent_id = $post->post_parent;
        $newposts[$post_key] = $item;
    }
    $posts = $newposts;

//    $post_type_object = get_post_type_object($post_type_name);

    if (!$posts)
        $error = '<li id="error">' . $post_type['args']->labels->not_found . '</li>';

    $walker = new GantryAssignmentWalker;
    $args['walker'] = $walker;
    $args['assignments'] = $assignments;

    ?>
    <div id="posttype-<?php echo $post_type_name; ?>" class="posttypediv">
        <ul id="<?php echo $post_type_name; ?>checklist"
            class="list:<?php echo $post_type_name?> categorychecklist form-no-clear">
        <?php
        $checkbox_items = gantry_walk_assignment_tree($posts, 0, (object) $args);
        echo $checkbox_items;
        ?>
        </ul>
    </div><!-- /.posttypediv -->
    <?php

}

/**
 * Displays a metabox for a taxonomy menu item.
 *
 * @since 3.0.0
 *
 * @param string $object Not used.
 * @param string $taxonomy The taxonomy object.
 */
function gantry_assignment_item_post_type_taxonomies_meta_box($object, $post_type, $assignments) {
    $post_type_name = $post_type['args']->name;

    $args = array(
        'child_of' => 0,
        'exclude' => '',
        'hide_empty' => false,
        'hierarchical' => 1,
        'include' => '',
        'include_last_update_time' => false,
        'order' => 'ASC',
        'orderby' => 'name',
        'pad_counts' => false,
    );

    $terms_list = array();

    $taxonomies = get_object_taxonomies($post_type_name);
    foreach ($taxonomies as $taxonomy_name) {
        $taxomony = get_taxonomy($taxonomy_name);

        $terms = get_terms($taxonomy_name, $args);

        $newterms = array();
        foreach ($terms as $term_key => $term) {
            $item = new AssignmentItem();
            $item->archetype = $post_type['args']->archetype;
            $item->type = $taxonomy_name;
            $item->id = $term->term_id;
            $item->single_label = $post_type['args']->single_label;
            $item->title = $term->name;
            $item->preview_url = get_term_link($term, $term->taxonomy);
            $item->parent_id = $term->parent;
            $newterms[$term_key] = $item;
        }

        $terms_list[$taxomony->labels->name] = $newterms;
    }

    if (empty($terms_list) || is_wp_error($terms)) {
        echo '<p>' . _g('No items.') . '</p>';
        return;
    }

    $walker = new GantryAssignmentWalker();
    $args['walker'] = $walker;
    $args['assignments'] = $assignments;
    
    ?>
    <div id="posttype-terms-<?php echo $post_type_name; ?>" class="posttypetermsdiv">
        <ul id="<?php echo $post_type_name; ?>-terms-checklist"
            class="list:<?php echo $post_type_name?>-terms categorychecklist form-no-clear">
        <?php
        foreach ($terms_list as $tax_name => $terms){
            $item_output = ' - ' .  '<a class="no-link-item sub-list-label">' . $tax_name . '</a>';
            echo $item_output;
            echo gantry_walk_assignment_tree($terms, 0, (object) $args);
        }
        ?>
        </ul>
    </div><!-- /.taxonomydiv -->
    <?php

}

/**
 * Creates metaboxes for any taxonomy menu item.
 *
 * @since 3.0.0
 */
function gantry_assignment_taxonomy_meta_boxes() {
    global $gantry_override_types;
    $taxonomies = get_taxonomies(array('show_ui' => true), 'object');
    if (!$taxonomies)
        return;
    $taxonomies = array_copy($taxonomies);

    foreach ($taxonomies as $tax) {
        $tax = apply_filters('gantry_assignment_meta_box_object', $tax);
        if ($tax) {
            $type = new AssignmentType();
            $type->name = $tax->name;
            $type->archetype = "taxonomy";
            $type->type_label = sprintf(_g('Taxonomy: %s'), $tax->labels->name);
            $type->type = $tax->name;
            $type->single_label = sprintf(_g('Taxonomy: %s'), $tax->labels->singular_name);
            $gantry_override_types[$type->archetype . '::' . $type->type] = $type;
            add_meta_box($type->archetype . '_' . $type->type, $type->type_label, 'gantry_assignment_item_taxonomy_meta_box', 'gantry_assignments', 'panel', 'default', $type);
        }
    }
}

/**
 * Creates metaboxes for any taxonomy menu item.
 *
 * @since 3.0.0
 */
function gantry_assignment_archives_meta_boxes() {
    global $gantry_override_types;
    $taxonomies = get_taxonomies(array('show_ui' => true), 'object');

    if (!$taxonomies)
        return;

    $taxonomies = array_copy($taxonomies);

    foreach ($taxonomies as $tax) {
        $tax = apply_filters('gantry_assignment_meta_box_object', $tax);
        if ($tax) {
            $type = new AssignmentType();
            $type->name = $tax->name;
            $type->archetype = "archive";
            $type->type_label = sprintf(_g('Archives: %s'), $tax->labels->name);
            $type->type = $tax->name;
            $type->single_label = sprintf(_g('Archive: %s'), $tax->labels->singular_name);
            $gantry_override_types[$type->archetype . '::' . $type->type] = $type;
            add_meta_box($type->archetype . '_' . $type->type, $type->type_label, 'gantry_assignment_item_taxonomy_meta_box', 'gantry_assignments', 'panel', 'default', $type);
        }
    }
}

/**
 * Displays a metabox for a taxonomy menu item.
 *
 * @since 3.0.0
 *
 * @param string $object Not used.
 * @param string $taxonomy The taxonomy object.
 */
function gantry_assignment_item_taxonomy_meta_box($object, $taxonomy, $assignments) {
    $taxonomy_name = $taxonomy['args']->name;

    $args = array(
        'child_of' => 0,
        'exclude' => '',
        'hide_empty' => false,
        'hierarchical' => 1,
        'include' => '',
        'include_last_update_time' => false,
        'order' => 'ASC',
        'orderby' => 'name',
        'pad_counts' => false,
    );

    $terms = get_terms($taxonomy_name, $args);

    if (!$terms || is_wp_error($terms)) {
        echo '<p>' . _g('No items.') . '</p>';
        return;
    }

    $walker = new GantryAssignmentWalker();
    $args['walker'] = $walker;
    $args['assignments'] = $assignments;

    $newterms = array();
    foreach ($terms as $term_key => $term) {
        $item = new AssignmentItem();
        $item->archetype = $taxonomy['args']->archetype;
        $item->type = $taxonomy_name;
        $item->id = $term->term_id;
        $item->single_label = $taxonomy['args']->single_label;
        $item->title = $term->name;
        $item->preview_url = get_term_link($term, $term->taxonomy);
        $item->parent_id = $term->parent;
        $newterms[$term_key] = $item;
    }
    $terms = $newterms;

    ?>
    <div id="taxonomy-<?php echo $taxonomy_name; ?>" class="taxonomydiv">
        <ul id="<?php echo $taxonomy_name; ?>checklist"
            class="list:<?php echo $taxonomy_name?> categorychecklist form-no-clear">
        <?php
        echo gantry_walk_assignment_tree($terms, 0, (object) $args);
        ?>
        </ul>
    </div><!-- /.taxonomydiv -->
    <?php

}


function gantry_assignment_menus_meta_boxes() {
    $menus = wp_get_nav_menus(array('orderby' => 'name'));
    if (!$menus)
        return;

    foreach ($menus as $menu) {
        $menu = apply_filters('gantry_assignment_meta_box_object', $menu);
        if ($menu) {
            $type = new AssignmentType();
            $type->name = $menu->name;
            $type->archetype = "menu";
            $type->type_label = sprintf(_g('Menu: %s'), $menu->name);
            $type->type = $menu->name;
            $type->single_label = sprintf(_g('Menu: %s'), $menu->name);
            add_meta_box($type->archetype . '_' . $type->type, sprintf(_g('Menu: %s'), $type->name), 'gantry_assignment_item_menu_meta_box', 'gantry_assignments', 'panel', 'default', $type);
        }
    }

}

function gantry_assignment_item_menu_meta_box($object, $menu, $assignments) {
    $menu_name = $menu['args']->name;

    $args = array(
        'child_of' => 0,
        'exclude' => '',
        'hide_empty' => false,
        'hierarchical' => 1,
        'include' => '',
        'include_last_update_time' => false,
        'order' => 'ASC',
        'orderby' => 'name',
        'pad_counts' => false,
    );

    $menu_items = wp_get_nav_menu_items($menu_name);

    if (!$menu_items || is_wp_error($menu_items)) {
        echo '<p>' . _g('No items.') . '</p>';
        return;
    }

    foreach ($menu_items as $menu_item) {
        $menu_item->menu_id = $menu_name;
    }

    $menu_entires = array();
    foreach ($menu_items as $menu_item) {
        $item = new AssignmentItem();
        $item->archetype = $menu['args']->archetype;
        $item->type = $menu_name;
        $item->id = $menu_item->ID;
        $item->single_label = $menu['args']->single_label;
        $item->parent_id = empty($menu_item->menu_item_parent) ? get_post_meta($menu_item->ID, '_menu_item_menu_item_parent', true) : $menu_item->menu_item_parent;

        // Get the title and url info
        $menu_item->object_id = empty($menu_item->object_id) ? get_post_meta($menu_item->ID, '_menu_item_object_id', true) : $menu_item->object_id;
        $menu_item->object = empty($menu_item->object) ? get_post_meta($menu_item->ID, '_menu_item_object', true) : $menu_item->object;
        $menu_item->type = empty($menu_item->type) ? get_post_meta($menu_item->ID, '_menu_item_type', true) : $menu_item->type;
        if ('post_type' == $menu_item->type) {
            $object = get_post_type_object($menu_item->object);
            $menu_item->url = get_permalink($menu_item->object_id);
            $original_object = get_post($menu_item->object_id);
            $original_title = $original_object->post_title;
            $menu_item->title = '' == $menu_item->post_title ? $original_title : $menu_item->post_title;

        } elseif ('taxonomy' == $menu_item->type) {
            $object = get_taxonomy($menu_item->object);
            $term_url = get_term_link((int) $menu_item->object_id, $menu_item->object);
            $menu_item->url = !is_wp_error($term_url) ? $term_url : '';
            $original_title = get_term_field('name', $menu_item->object_id, $menu_item->object, 'raw');
            $menu_item->title = '' == $menu_item->post_title ? $original_title : $menu_item->post_title;

        } else {
            $menu_item->title = $menu_item->post_title;
            $menu_item->url = empty($menu_item->url) ? get_post_meta($menu_item->ID, '_menu_item_url', true) : $menu_item->url;
        }

        $item->title = $menu_item->title;
        $menu_entires[] = $item;
    }

    $walker = new GantryAssignmentWalker();
    $args['assignments'] = $assignments;

    ?>
    <div id="menu-<?php echo $menu_name; ?>" class="menudiv">
        <ul id="<?php echo $menu_name; ?>checklist"
            class="list:<?php echo $menu_name?> categorychecklist form-no-clear">
        <?php
        $args['walker'] = $walker;
        echo gantry_walk_assignment_tree($menu_entires, 0, (object) $args);
        ?>
        </ul>
    </div><!-- /.menudiv -->
    <?php

}

function gantry_assignment_template_pages_meta_boxes() {
    $type = new AssignmentType();
    $type->archetype = "templatepage";
    $type->type_label = _g('Template Page Types');
    $type->single_label = _g('Template Page Type');
    $type->name = _g('Template Page Type');
    $menu = apply_filters('gantry_assignment_meta_box_object', $type);
    add_meta_box($type->archetype, _g('Template Page Types'), 'gantry_assignment_item_template_pages_meta_box', 'gantry_assignments', 'panel', 'high', $type);
}

function gantry_assignment_item_template_pages_meta_box($object, $box, $assignments) {
    $templatepages_name = $box['args']->name;
    $args = array(
        'child_of' => 0,
        'exclude' => '',
        'hide_empty' => false,
        'hierarchical' => 0,
        'include' => '',
        'include_last_update_time' => false,
        'order' => 'ASC',
        'orderby' => 'name',
        'pad_counts' => false,
    );
    $page_types = array(
        '404' => _g('404 Not Found Page'),
        'search' => _g('Search Page'),
        'tax' => _g('Taxonomy Archive Page'),
        'front_page' => _g('Front Page'),
        'home' => _g('Home Page'),
        'attachment' => _g('Attachment Page'),
        'single' => _g('Single Post Page'),
        'page' => _g('PAGE page'),
        'category' => _g('Category Archive Page'),
        'tag' => _g('Tag Archive Page'),
        'author' => _g('Author Page'),
        'date' => _g('Date Archive Page'),
        'preview' => _g('Preview Page'),
        'comments_popup' => _g('Comments Popup Page')
    );

   $page_types = apply_filters('gantry_admin_page_types', $page_types);

    ksort($page_types);
    $template_page_types = array();
    foreach ($page_types as $page_type_id => $page_type_name) {
        $item = new AssignmentItem();
        $item->archetype = $box['args']->archetype;
        $item->type = $page_type_id;
        $item->title = $page_type_name;
        $item->parent_id = 0;
        $item->single_label = sprintf(_g('Template Page Type'), $page_type_name);
        $template_page_types[] = $item;
    }
    $args['assignments'] = $assignments;
    $walker = new GantryAssignmentWalker();

    ?>
    <div id="templatepage-<?php echo $templatepages_name; ?>" class="templatepagediv">
        <ul id="<?php echo $templatepages_name; ?>checklist"
            class="list:<?php echo $templatepages_name?> categorychecklist form-no-clear">
        <?php
        $args['walker'] = $walker;
        echo gantry_walk_assignment_tree($template_page_types, 0, (object) $args);
        ?>
        </ul>
    </div>
    <?php

}


function gantry_walk_assignment_tree($items, $depth, $r) {
    $walker = (empty($r->walker)) ? new GantryAssignmentWalker : $r->walker;
    $args = array($items, $depth, $r);
    return call_user_func_array(array(&$walker, 'walk'), $args);
}

function do_assignment_meta_boxes($page, $context, $object, $assignments = array(), &$assignment_info = array()) {
    global $wp_meta_boxes;
    global $gantry_override_assignment_info;
    static $already_sorted = false;


    $hidden = get_hidden_meta_boxes($page);

    printf('<div id="%s-sortables" class="meta-box-sortables">', htmlspecialchars($context));

    $i = 0;
    do {
        // Grab the ones the user has manually sorted. Pull them out of their previous context/priority and into the one the user chose
        if (!$already_sorted && $sorted = get_user_option("meta-box-order_$page")) {
            foreach ($sorted as $box_context => $ids)
                foreach (explode(',', $ids) as $id)
                    if ($id)
                        add_meta_box($id, null, null, $page, $box_context, 'sorted');
        }
        $already_sorted = true;

        if (!isset($wp_meta_boxes) || !isset($wp_meta_boxes[$page]) || !isset($wp_meta_boxes[$page][$context]))
            break;

        $skip_checkbox = array('templatepage', 'menu','taxonomy');

        foreach (array('high', 'sorted', 'core', 'default', 'low') as $priority) {
            if (isset($wp_meta_boxes[$page][$context][$priority])) {
                foreach ((array) $wp_meta_boxes[$page][$context][$priority] as $box) {
                    if (false == $box || !$box['title'])
                        continue;
                    $i++;
                    $style = '';

                    //echo '<div id="' . $box['id'] . '" class="postbox ' . postbox_classes($box['id'], $page) . $hidden_class . '" ' . '>' . "\n";

                    $data = $box['args'];
                    $assigned = "";
                    $checked = "";
                    if (isset($assignments[$data->archetype]) && isset($assignments[$data->parchetype][$data->type]) && is_bool($assignments[$data->archetype][$data->type]) && $assignments[$data->archetype][$data->type] === true) {
                        $assigned = " added";
                        $checked = ' checked="checked"';
                        $data->single_label = _g('Type');
                        $gantry_override_assignment_info[$data->archetype . '::' . $data->type] = $data;
                    }


                    echo '<div id="' . $box['id'] . '"  class="assignments-block">' . "\n";
                    echo "	<h2 class='" . strtolower(str_replace(" ", "-", $box['title'])) . "'>\n";
                    if (!in_array($data->archetype, $skip_checkbox)) {
                        echo "		<label class=\"rokchecks global menu-item-" . $box['id'] . "\">\n";
                        echo '			<input class="assignment-checkbox global" ' . $checked . ' type="checkbox" name="menu-item-' . $box['id'] . '" value="' . $box['id'] . '" />' . "\n";
                        echo " 		</label>\n";
                    }
                    echo '		<span class="' . $data->archetype . '::' . $data->type . '">' . $box['title'] . "</span></h2>\n";
                    echo '	<div class="inside' . $assigned . '">' . "\n";
                    call_user_func($box['callback'], $object, $box, $assignments);
                    echo "	</div>\n";
                    echo "  <div class=\"clr\"></div>\n";
                    echo "	<div class=\"footer-block\">\n";
                    echo "		<div class=\"select-all\"><a href=\"#\">Select All</a></div>\n";
                    echo "		<div class=\"add-button\"><input class=\"button-secondary add-to-assigned\" type=\"button\" value=\"Add to Assigned\" /></div>\n";
                    echo "	</div>\n";
                    echo "</div>\n";

                    //echo "</div>\n";
                }
            }
        }
    } while (0);

    echo "</div>";

    return $i;

}

class AssignmentItem {
    var $archetype;
    var $type;
    var $id;
    var $parent_id;
    var $title;
    var $preview_url;
    var $single_label;
}

class AssignmentType {
    var $archetype;
    var $type;
    var $title;
    var $type_label;
    var $single_label;
    var $name;
}

/**
 * Merges any number of arrays of any dimensions, the later overwriting
 * previous keys, unless the key is numeric, in whitch case, duplicated
 * values will not be added.
 *
 * The arrays to be merged are passed as arguments to the function.
 *
 * @access public
 * @return array Resulting array, once all have been merged
 */
function array_merge_replace_recursive() {
    // Holds all the arrays passed
    $params = & func_get_args();

    // First array is used as the base, everything else overwrites on it
    $return = array_shift($params);

    // Merge all arrays on the first array
    foreach ($params as $array) {
        foreach ($array as $key => $value) {
            // Numeric keyed values are added (unless already there)
            if (is_numeric($key) && (!in_array($value, $return))) {
                if (is_array($value)) {
                    $return [] = array_merge_replace_recursive($return [$$key], $value);
                } else {
                    $return [] = $value;
                }

                // String keyed values are replaced
            } else {
                if (isset ($return [$key]) && is_array($value) && is_array($return [$key])) {
                    $return [$key] = array_merge_replace_recursive($return [$$key], $value);
                } else {
                    $return [$key] = $value;
                }
            }
        }
    }

    return $return;
}

/**
 * make a recursive copy of an array
 *
 * @param array $aSource
 * @return array    copy of source array
 */
function array_copy($aSource) {
    // check if input is really an array
    if (!is_array($aSource)) {
        throw new Exception("Input is not an Array");
    }

    // initialize return array
    $aRetAr = array();

    // get array keys
    $aKeys = array_keys($aSource);
    // get array values
    $aVals = array_values($aSource);

    // loop through array and assign keys+values to new return array
    for ($x = 0; $x < count($aKeys); $x++) {
        // clone if object
        if (is_object($aVals[$x])) {
            $aRetAr[$aKeys[$x]] = clone $aVals[$x];
            // recursively add array
        } elseif (is_array($aVals[$x])) {
            $aRetAr[$aKeys[$x]] = array_copy($aVals[$x]);
            // assign just a plain scalar value
        } else {
            $aRetAr[$aKeys[$x]] = $aVals[$x];
        }
    }

    return $aRetAr;
}